// This code is hosted on http://code.google.com/p/lenthorp/
// Freely available for use in applications, but should NOT be modified
// Email all comments to lenthorpresearch@gmail.com

#ifndef quantlib_tradingengine_hpp
#define quantlib_tradingengine_hpp

#include <ql/time/date.hpp>
#include <qt/utilities/daterateholder.hpp>
#include <qt/tradingportfolio.hpp>
#include <qt/tradeorder.hpp>
#include <string>
#include <map>
#include <boost/shared_ptr.hpp>
#include <vector>

namespace QuantLib {


	class Performance {
		public:
			Performance() {}
			Performance(Date startDate)
				: _startDate(startDate) {
				_obs = DateRateHolder();
			}

			void setObservation(Date d, Real v) {
				_obs.setElement(d, v);
			}

			DateRateHolder getObservations() {
				return _obs;
			}

			Date getStartDate() {
				return _startDate;
			}
		
		private:
			Date _startDate;
			DateRateHolder _obs;
	};


	//class Portfolio {
	//	typedef std::pair<std::string, Real> positionElement;
	//	public:
	//		Portfolio(Real initialCash) {
	//			_positions.insert(positionElement("cash",initialCash));
	//		}

	//		// Accrue any dividends/interest and decide what to do with it
	//		void updatePositions() {
	//		}

	//		void placeOrder(TradeOrder order, Real value) {
	//			if (order.buyOrSell == TradeOrder::OrderType::buy) {
	//				_positions["cash"] -= value * order.notional;
	//				_positions[order.assetName] += order.notional;
	//			}
	//			else {
	//				_positions["cash"] += value * order.notional;
	//				_positions[order.assetName] -= order.notional;
	//			}
	//		}

	//		std::vector<std::string> getPositionNames() {
	//			std::vector<std:string> names = std::vector<std:string>();
	//			std::map<std::string, Real>::iterator it;
	//			for (it =_positions.begin(); it != _positions.end(); ++it)
	//				names.push_back(it->first);
	//			return names;
	//		}

	//		bool isAllCash() {
	//			std::map<std::string, Real>::iterator it;
	//			for (it =_positions.begin(); it != _positions.end(); ++it) {
	//				if (it->first != "cash" && it->second > 1e-10)
	//					return false;
	//			}
	//			return true;
	//		}

	//		Real getAvailableCash() {
	//			return _positions["cash"];
	//		}

	//		Real getCurrentValue(DateMultiRateHolder assetPrices, Size index) {
	//			std::map<std::string, Real>::iterator it;
	//			Real val = 0.0;
	//			for (it =_positions.begin(); it != _positions.end(); ++it) {
	//				if (it->first == "cash")
	//					val += it->second;
	//				else
	//					val += it->second * assetPrices.getElementRate(index, it->first);
	//			}
	//			return val;
	//		}


	//	private:

	//		std::map<std::string, Real> _positions;

	//};

	class AssetAnalytics {
		public:
			AssetAnalytics() {};
			Real getCurrentMovingAverage(Portfolio& p, std::string assetName, Size nberDays) {
				return 0.0;
			}
			Real getCurrentAssetValue(Portfolio& p, std::string assetName) {
				return p.getCurrentAssetValue(assetName);
			}
	};

	// Abstract base class
	class TradingStrategy {
		public:
			TradingStrategy() {
			}

			// Specific to each trading strategy
			virtual std::vector<TradeOrder> getAction(Portfolio& p) = 0;

		protected:
			std::map<std::string, std::string> _assetNamesMap;
			AssetAnalytics _assetAnalytics;
	};



	
	class TradingEngine {

		public:
			TradingEngine(Portfolio p, boost::shared_ptr<TradingStrategy> s)
				: _initialPortfolio(p),
				_strategy(s) { }

			std::vector<Performance> performBacktest(std::vector<Date> startDates, BigInteger backtestingLengthDays);

		protected:
			
			Portfolio _initialPortfolio;
			boost::shared_ptr<TradingStrategy> _strategy;
			
	};

}


#endif